/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 * Description: Pipe Runtime
 */

#ifndef HIAI_API_DATA_HOLDER_H
#define HIAI_API_DATA_HOLDER_H
#include <cstdint>

#include "base/maybe.h"
#include "graph/graph_api_export.h"

namespace hiai {
using TypeId = size_t;

template <typename T>
class GRAPH_API_EXPORT TypeIdentity {
public:
    static TypeId Value()
    {
        static TypeIdentity<T> inst;
        return reinterpret_cast<TypeId>(&inst);
    }
};

class IDataHolder {
public:
    virtual ~IDataHolder() = default;
};

template <typename T>
class DataHolder : public IDataHolder {
public:
    DataHolder(T data) : typeId_(TypeIdentity<T>::Value()), data_(std::move(data))
    {
    }

    hiai::Maybe<T> As() const
    {
        if (typeId_ != TypeIdentity<T>::Value()) {
            return hiai::Maybe<T>(hiai::NULL_MAYBE);
        }
        return hiai::Maybe<T>(data_);
    }

private:
    TypeId typeId_;
    T data_;
};

class Any {
public:
    Any() : dataHolder_(nullptr)
    {
    }

    ~Any() = default;

    template <typename T>
    using EnableIfIsObjectAndNotAny = typename std::enable_if<!std::is_same<typename std::decay<T>::type, Any>::value &&
        std::is_object<T>::value>::type;

    template <typename T, typename = EnableIfIsObjectAndNotAny<T>>
    static Any MakeAny(T data)
    {
        auto dataHolder = std::shared_ptr<DataHolder<T>>(new (std::nothrow) DataHolder<T>(std::move(data)));
        return Any(std::move(dataHolder));
    }

    bool HasValue()
    {
        return dataHolder_ != nullptr;
    }

    template <typename T>
    hiai::Maybe<T> AnyCast() const
    {
        if (dataHolder_ == nullptr) {
            return hiai::Maybe<T>(hiai::NULL_MAYBE);
        }
        return (static_cast<DataHolder<T>*>(dataHolder_.get()))->As();
    }

private:
    Any(std::shared_ptr<IDataHolder> dataHolder) : dataHolder_(std::move(dataHolder))
    {
    }

private:
    std::shared_ptr<IDataHolder> dataHolder_;
};

} // namespace hiai
#endif